package com.nio.c5_网络编程.write;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;

/**
 * @author： pang-yun
 * @date： 2021-09-09 10:49
 * 功能目的 ：用于 可写事件
 * <p>
 * - 如输出 3000000 数据时 数据量较大 缓存区有限  有时不能写入  写缓冲区会产生阻塞情况   此时可以 去处理 读事件
 */

public class WriteServer {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        SelectionKey sscKey = ssc.register(selector, SelectionKey.OP_ACCEPT, null);
        ssc.bind(new InetSocketAddress(9999));

        while (true) {
            selector.select();
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isAcceptable()) {
                    // 此程序中 只有 一个serverSocketChannel 因此不需要再由 key中来获取  可以直接拿来使用
                    //  ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = ssc.accept();
                    socketChannel.configureBlocking(false);
                    ByteBuffer byteBuffer = ByteBuffer.allocate(16);
                    SelectionKey scKey = socketChannel.register(selector, SelectionKey.OP_READ, byteBuffer);

                    // 1.向客户端 发送大量数据
                    StringBuilder stringBuilder = new StringBuilder();

                    for (int i = 0; i < 5000000; i++) {
                        stringBuilder.append("a");
                    }
                    ByteBuffer buffer = Charset.defaultCharset().encode(stringBuilder.toString());


                  /*  while (buffer.hasRemaining()){
                        //2. 返回值代表实际写入的字节数
                        int write = socketChannel.write(buffer);
                        System.out.println(write);
                    }*/


                    // 2. 返回值代表实际写入的字节数
                    int write = socketChannel.write(buffer);
                    System.out.println(write);

                    //3. 判断是否有剩余内容
                    if (buffer.hasRemaining()) {
                        // 4.关注可写事件
                        scKey.interestOps(scKey.interestOps() + SelectionKey.OP_WRITE);
                        //5. 把未写完的数据作为附件 与scKey 关联
                        scKey.attach(buffer);
                    }

                } else if (key.isWritable()) {
                    ByteBuffer buf = (ByteBuffer) key.attachment();
                    SocketChannel socketChannel= (SocketChannel) key.channel();
                    int write = socketChannel.write(buf);
                    System.out.println(write);
                    //6.附件 无数据时 应该将附件丢弃 减小性能影响
                    if (!buf.hasRemaining()){
                        key.attach(null);  //将附件 变为null
                        key.interestOps(key.interestOps()-SelectionKey.OP_WRITE); // 不需要再关注写事件
                    }
                }
            }
        }
    }
}
